#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <memory>
#include <strings.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"

namespace SocketModule
{
    using namespace LogModule;

    const static int defaultbacklog = 10;
    const static int defaultfd = -1;
    const static int defaultcap = 1024;
    class Socket
    {
    public:
        using ptr = std::shared_ptr<Socket>;
        ~Socket() {}
        virtual void CreateSocket() = 0;
        virtual void BindSocket(uint16_t port, std::string ip) = 0;
        virtual void ListenSocket(int backlog) = 0;
        virtual ptr AcceptConnect(uint16_t *peerport, std::string *peerip) = 0;
        virtual void ConnectServer(uint16_t port, std::string ip) = 0;
        virtual bool ReadMessage(std::string *msg) = 0;
        virtual void WriteMessage(std::string *msg) = 0;
        virtual int GetSocket() = 0;

    public:
        void CreateTcpSocket(uint16_t port, std::string ip, int backlog = defaultbacklog)
        {
            CreateSocket();
            BindSocket(port, ip);
            ListenSocket(backlog);
        }

        void ConnectTcpSocket(uint16_t port, std::string ip)
        {
            CreateSocket();
            ConnectServer(port, ip);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int socket = defaultfd) : _socket(socket)
        {
        }
        void CreateSocket() override
        {
            _socket = socket(AF_INET, SOCK_STREAM, 0);
            if (_socket < 0)
            {
                LOG(LogLevel::FATAL) << "套接字创建失败 ： " << strerror(errno);
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "监听套接字创建成功！";
        }

        void BindSocket(uint16_t port, std::string ip) override
        {
            sockaddr_in local;
            bzero(&local, sizeof(local));
            int ret = inet_pton(AF_INET, ip.c_str(), &local.sin_addr);
            if (ret <= 0)
            {
                LOG(LogLevel::FATAL) << "inet_pton失败 ： " << strerror(errno);
                exit(INET_PTON);
            }
            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            int n = ::bind(_socket, (sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind失败 ： " << strerror(errno);
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "监听套接字bind成功！";
        }

        void ListenSocket(int backlog) override
        {
            int n = listen(_socket, backlog);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen失败 ： " << strerror(errno);
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "监听套接字listen成功！";
        }

        ptr AcceptConnect(uint16_t *peerport, std::string *peerip) override
        {
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int socket = accept(_socket, (sockaddr *)&peer, &len);
            if (socket < 0)
            {
                LOG(LogLevel::FATAL) << "accept失败 ： " << strerror(errno);
                exit(ACCEPT_ERR);
            }
            LOG(LogLevel::INFO) << "监听套接字accept成功！";
            char buffer_ip[INET_ADDRSTRLEN];
            const char *ret = inet_ntop(AF_INET, &peer.sin_addr, buffer_ip, sizeof(peer));
            if (ret == nullptr)
            {
                LOG(LogLevel::FATAL) << "inet_ntop失败 ： " << strerror(errno);
                exit(INET_NTOP);
            }
            *peerip = ret;
            *peerport = ntohs(peer.sin_port);
            return std::make_shared<TcpSocket>(socket);
        }

        void ConnectServer(uint16_t port, std::string ip) override
        {
            sockaddr_in server;
            bzero(&server, sizeof(server));
            int ret = inet_pton(AF_INET, ip.c_str(), &server.sin_addr);
            if (ret <= 0)
            {
                LOG(LogLevel::FATAL) << "inet_ntop失败 ： " << strerror(errno);
                exit(INET_PTON);
            }
            server.sin_port = htons(port);
            server.sin_family = AF_INET;
            int n = connect(_socket, (sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "connect失败 ： " << strerror(errno);
                exit(CONNECT_ERR);
            }
        }

        bool ReadMessage(std::string *msg) override
        {
            //此处可改用 += 的方式来接收内容，否则容易消息读不完全。
            char buffer[defaultcap];
            int n = read(_socket, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                *msg += buffer;
                LOG(LogLevel::DEBUG) << "socket: " << _socket;
                return true;
            }
            else if (n == 0)
            {
                LOG(LogLevel::WARNING) <<  "socket: " <<_socket << "read失败 ： 对方关闭IO流！" << strerror(errno);
                return true;
            }
            else
            {
                LOG(LogLevel::FATAL) << "read失败 ： " << strerror(errno);
                return false;
            }
        }

        void WriteMessage(std::string *msg) override
        {
            int n = write(_socket, msg->c_str(), msg->size());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "write失败 ： " << strerror(errno);
                exit(WRITE_ERR);
            }
        }

        void SetReuseAddr()
        {
            int opt = 1;
            setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        }

        void Close()
        {
            if (_socket != defaultfd)
            {
                close(_socket);
                _socket = defaultfd;
            }
        }

        int GetSocket() override
        {
            return _socket;
        }

        ~TcpSocket()
        {
            // Close();
        }

    private:
        int _socket; // 监听套接字 / 普通套接字
    };
}